Registering your C++ components to the Lua Script API with tolua++
In this page we discuss how to register your C++ components so you can use their functions in Lua scripts. We'll also explain how to register other things like regular classes or subsystems. Requisites This section assumes you already know how to create your own components in C++ extending Urho3D's functionality (see TODO: Link). You should also be programming the engine initialisation code (your main application) in C++ or at least being able to modify Urho3DPlayer's code in order to call the binding functions. An introduction to tolua++ Tolua++ is a program/library that comes with Urho3D as a tool. Its purpose is to read header-like C/C++ files (in their wording, cleaned header files) and output C code binding the C++ side to lua's memory. In order to work with tolua++ we need, thus, to create a pkg file for each class we're willing to export. For now, let's have a look at a pkg file for the AnimatedModel component: $#include "Graphics/AnimatedModel.h" class AnimatedModel : public StaticModel { void SetModel(Model* model); AnimationState* AddAnimationState(Animation* animation); void RemoveAnimationState(Animation* animation); void SetAnimationLodBias(float bias); void SetUpdateInvisible(bool enable); void ResetMorphWeights(); Skeleton& GetSkeleton(); unsigned GetNumAnimationStates() const; AnimationState* GetAnimationState(Animation* animation) const; float GetAnimationLodBias() const; bool GetUpdateInvisible() const; float GetMorphWeight(unsigned index) const; bool IsMaster() const; tolua_property__get_set Model* model; tolua_readonly tolua_property__get_set unsigned numMorphs; tolua_readonly tolua_property__is_set bool master; }; The cleaned header file code looks pretty much like a C++ normal class declaration. You can add function declarations and some special tolua++ code to define properties, so you can access fields without having to explicitly use getters and setters. For this guide, we'll stick with what can be done by using regular tolua++ bindings. Some advice on weak typing It's important to note that, due to the weak-typed nature of Lua, and most of the Urho types being registered as user data, it's difficult to properly map C++ types and classes to Lua tables, as they are essentially different things. While tolua++ does a great job in automatically binding our C++ functions and classes to Lua, Lua has no way to differentiate from another when passing parameters around. Thus a pointer (or reference) to Node could be passed to a function asking for a pointer to a Scene, and thus cause undefined behaviour. The bindings generated with tolua++ are not always perfectly checked and if you're not careful you can crash your application by doing something as simple as player.GetPlayerID() instead of player:GetPlayerID() (Note how the second one uses a colon to implicitly pass the player as the "self"). Using URHO3D_SAFE_LUA=1 flag during compile time adds some intermediate code checking for most common errors, and is highly recommended in a developement build. But that doesn't completely fix the issue. Our example component We'll use an example component in order to illustrate the process of registering it to the Lua script API. The component's code (only the header file is included) is as follows: #pragma once #include #include #include using namespace Urho3D; class PlayerController : public LogicComponent { OBJECT(PlayerController) public: PlayerController(Context* context); static void RegisterObject(Context* context); virtual void ApplyAttributes(); virtual void Start(); virtual void Update(float timeStep); int GetPlayerID(); void SetPlayerID(int id); Vector3 GetPlayerPosition(); String GetPlayerName(); void SetPlayerName(String name); void Jump(float jumpForce, Vector3 jumpDirection); private: } As you can see, the player is a regular LogicComponent with its start and update functions. After those, some new functions are added for this particular component. Our goal is now to make Lua Aware of PlayerController and its internal functions such as Jump. Binding our component to Lua Binding c++ to lua is usually a cumbersome task. However, thanks to tolua++ we'll only have to write header files with minor modifications most of the time. For our case, we want to be able to register the following things to Lua: * The PlayerController component itself, which Lua will get as a usertype * The functions GetPlayerID, SetPlayerID, GetPlayerPosition, GetPlayerName, SetPlayerName and Jump * Properties that can hide the getter/setter functions like in AngelScript or other languages like C#. First we'll focus on points 1 and 2. Create a new file PlayerController.pkg and place it next to PlayerController.h and PlayerController.cpp (which is assumed to be implemented). The content should be as follows: $using namespace Urho3D; $#include "PlayerController.h" class PlayerController : public LogicComponent { public: int GetPlayerID(); void SetPlayerID(int id); Vector3 GetPlayerPosition(); String GetPlayerName(); void SetPlayerName(String name); void Jump(float jumpForce, Vector3 jumpDirection); } As you can see, the only thing we had to do is remove the C++ includes, and leave just the functions we didn't want to bind to Lua. As for the first two lines, anything starting by $ will get included as-is in the final source file, so the generated binder code will have those two lines (so it will use the namespace Urho3D and know about PlayerController class. In fact, those two statements are required for this to work. At this point, you should also go to the Urho3D source folder and copy ToCppHook.lua from Urho3D/LuaScript/pkgs. This file handles things like properly detecting the String type or adjusting to Urho's Getter/Setter naming conventions to automatically bind properties. More about that can be found on the next section, where we define the properties for our class. For convenience, you should also copy the tolua++ executable that Urho3D generated when building (under the Tools directory) into your current working directory. With the pkg file, we have everything we need to generate the binder code. To do so run: tolua++ -E YourProjectName -L ToCppHook.lua -o BindPlayerController.cpp PlayerController.pkg Of course, it's an inconvenience to have the tolua++ executable and all the pkg files right into your Source folder. It's up to the reader to adapt that command to his particular workflow. Just take into account that the -L, -o and the pkg filename are paths and should point to the respective files. That should have generated a file called BindPlayerController.cpp in the current directory. Inside the file there's the C++ code responsible for binding your class to the Lua API. Feel free to take a look at the code and see why tolua++ is such a useful tool. Right now we only need one last thing. Open BindPlayerController.cpp and you'll see that a bunch of global functions are declared on it. Right at the top you should find a line like this: /* Exported function */ TOLUA_API int tolua_PlayerController_open (lua_State* tolua_S); That is the binder function. As you can see it's defined as a global function. Calling that by passing the the proper lua_State makes the LuaScript subsystem aware of our class. To do so, in C++, somewhere before registering your custom components and registering the lua subsystem in your main application code, you should add the following code: LuaScript* lua = context->GetSubsystem(); tolua_PlayerController_open(lua->GetState()); And of course, to make your current file aware that there's a global function called tolua_PlayerController_open in another source file, you should add the following code in the global scope: extern int tolua_PlayerController_open (lua_State* tolua_S); With all this, you have successfully registered your component to the Lua API! Binding properties TODO Binding more than one class at the same time with includes TODO Make a custom-made subsystem available as a global variable TODO Further reading You can check the full documentation for tolua++ to get a better understanding of how tolua++ works and what additional things can you define. The documentation can be found at: https://www8.cs.umu.se/kurser/TDBD12/VT04/lab/lua/tolua%2B%2B.html